#include "Ogre7zCommon.h"

#include <7zCrc.h>

namespace Ogre
{

//-------------------------------------------------------------------------------------------------------------------------------------------------
static ISzAlloc gISzAllocImpl;
//-------------------------------------------------------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------------------------------------------------------
void SevenZipUtil::initialiseLibrary()
{
	bool sIsInitialised = false;

	// Is crc table created?
	if( sIsInitialised )
		return;

	// Create crc tables
	::CrcGenerateTable();

	gISzAllocImpl.Alloc = SevenZipUtil::allocImpl;
	gISzAllocImpl.Free = SevenZipUtil::freeImpl;

	sIsInitialised = true;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void* SevenZipUtil::allocImpl( void*, size_t size )
{
	return OGRE_MALLOC_SIMD( size, MEMCATEGORY_GENERAL );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void SevenZipUtil::freeImpl( void*, void* address )
{
	OGRE_FREE_SIMD( address, MEMCATEGORY_GENERAL );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
ISzAlloc* SevenZipUtil::getISzAlloc()
{
	return &gISzAllocImpl;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
std::string SevenZipUtil::getErrorString( SRes error )
{
	switch( error )
	{
	case SZ_OK:
		return "return OK";

	case SZ_ERROR_DATA:
		return "Data Error";

	case SZ_ERROR_MEM:
		return "Memory Error";

	case SZ_ERROR_CRC:
		return "CRC Error";

	case SZ_ERROR_UNSUPPORTED:
		return "Unsupported";

	case SZ_ERROR_PARAM:
		return "Param Error";

	case SZ_ERROR_INPUT_EOF:
		return "Input EOF Error";

	case SZ_ERROR_OUTPUT_EOF:
		return "Output EOF Error";

	case SZ_ERROR_READ:
		return "Read Error";

	case SZ_ERROR_WRITE:
		return "Write Error";

	case SZ_ERROR_PROGRESS:
		return "Progress";

	case SZ_ERROR_FAIL:
		return "Fail";

	case SZ_ERROR_THREAD:
		return "Thread Error";

	case SZ_ERROR_ARCHIVE:
		return "Archive Error";

	case SZ_ERROR_NO_ARCHIVE:
		return "No Archive Error";
	};

	return "Unknown Error";
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
bool SevenZipUtil::Utf16ToUtf8( unsigned char* dest, size_t* destLen, const short* src, size_t srcLen )
{
	static const unsigned char sUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };

	size_t destPos = 0, srcPos = 0;
	for(;;)
	{
		unsigned int numAdds;
		unsigned long value;
		if( srcPos == srcLen )
		{
			*destLen = destPos;
			return true;
		}

		value = src[srcPos++];
		if( value < 0x80 )
		{
			if( dest )
			{
				dest[destPos] = (char)value;
			}
			destPos++;
			continue;
		}

		if( value >= 0xD800 && value < 0xE000 )
		{
			unsigned long c2;
			if( value >= 0xDC00 || srcPos == srcLen )
				break;

			c2 = src[srcPos++];
			if( c2 < 0xDC00 || c2 >= 0xE000 )
				break;

			value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
		}

		for( numAdds = 1; numAdds < 5; numAdds++ )
		{
			if( value < (((UInt32)1) << (numAdds * 5 + 6)) )
				break;
		}

		if( dest )
		{
			dest[destPos] = (char)(sUtf8Limits[numAdds - 1] + (value >> (6 * numAdds)));
		}

		destPos++;
		do
		{
			numAdds--;
			if( dest )
			{
				dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F));
			}
			destPos++;
		}
		while( numAdds != 0 );
	}

	*destLen = destPos;
	return false;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
String SevenZipUtil::Utf16ToUtf8( const short* src, size_t srcLen )
{
	String ret;
	// Convert string
	Utf16ToUtf8( ret, src, srcLen );

	return ret;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void SevenZipUtil::Utf16ToUtf8( String& dest, const short* src, size_t srcLen )
{
	// Calc destination size
	size_t destLen = 0;
	Utf16ToUtf8( NULL, &destLen, src, srcLen ); 

	// Set destination buffer size
	dest.resize( destLen );

	// Convert string
	Utf16ToUtf8( (unsigned char*)dest.c_str(), &destLen, src, srcLen ); 
}

} // end of namespace Ogre
